use actix_web::{dev::Payload, web::{BufMut, BytesMut}, FromRequest};
use futures_util::StreamExt;
//use actix_web::dev::Decompress;
use serde::de::DeserializeOwned;
use std::{
    future::Future,
    marker::PhantomData,
    task::{ready, Poll},
};

use crate::error::{new_err, new_err_static, AppErr};

use super::utils::Cbor;

const CBOR_BODY_MAX_SIZE: usize = 2 * 1024 * 1024;

impl<T: DeserializeOwned + Unpin> FromRequest for Cbor<T> {
    type Error = AppErr;
    type Future = CborFut<T>;

    fn from_request(_req: &actix_web::HttpRequest, payload: &mut Payload) -> Self::Future {
        CborFut {
            _v: PhantomData,
          //  payload: Decompress::from_headers(payload.take(), req.headers()),
            payload: payload.take(),
            buf: BytesMut::new(),
        }
    }
}

pub struct CborFut<T> {
    _v: PhantomData<T>,
  //  payload: Decompress<Payload>,
    payload: Payload,
    buf: BytesMut,
}

impl<T: DeserializeOwned + Unpin> Future for CborFut<T> {
    type Output = Result<Cbor<T>, AppErr>;

    fn poll(
        self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Self::Output> {
        let this = self.get_mut();
        while let Some(item) = ready!(this.payload.poll_next_unpin(cx)) {
            match item {
                Ok(item) => {
                    if item.len() + this.buf.len() > CBOR_BODY_MAX_SIZE {
                        return Poll::Ready(Err(new_err_static("body large")));
                    }
                    this.buf.put_slice(&item);
                }
                Err(e) => return Poll::Ready(Err(new_err(e))),
            }
        }

        let body = serde_cbor::from_slice::<T>(&this.buf);
        match body {
            Err(e) => Poll::Ready(Err(new_err(e))),
            Ok(v) => Poll::Ready(Ok(Cbor(v)))
        }
    }
}
